#pragma once

#include "Common.hpp"
#include "PageMap.hpp"
#include "ShmServiceManger.hpp"
#include "LOG_CYC.hpp"

class ShmPageCache
{
public:
	// 获取实例的方法
	static ShmPageCache* GetInstance() {
		// 使用双重检查锁定来确保线程安全
		if (_instance == nullptr) {
			std::lock_guard<std::mutex> lock(_mutex);
			if (_instance == nullptr) {
				_instance = new ShmPageCache();
			}
		}
		return _instance;
	}


	// // 获取全局锁
	// std::mutex& GetPageMtx() {
	// 	return _pageMtx;
	// }


	// 获取一个k页的Span
	Span* NewSpan(size_t k) {
		assert(k > 0); // 确保请求的页数大于0

		// 超过Page最大管理页
		if (k > NPAGES - 1) {
			// 直接获取大于128页的共享内存文件
			ShmServiceNode* node = _shmManger->GetShmMemory(k << MY_PAGE_SHIFT);
			if (node == nullptr) {
				LOG_CYC(ERROR, "获取大小为: %ld 的内存失败", k); // 日志记录内存获取失败
				return nullptr;
			}

			Span* span = new Span;
			span->_headPtr = node->_shmSvr->GetShmHeader();
			span->_fileId = node->_shmFileId;
			span->_spanPos = 0;
			span->_n = k;
			span->_isUse = true;

			// 将span记录到映射中
			node->_pageMap[0] = span;

			return span;
		}

		// 检查第k个桶里是否有span, 有就直接拿走
		if (!_spanLists[k].Empty()) {
			// if (!_spanLists[k].empty()) {
			Span* kSpan = _spanLists[k].PopFront();
			// Span* kSpan = _spanLists[k].front();
			// _spanLists[k].pop_front();

			ShmServiceNode* node = _shmManger->GetShmNodeByFileId(kSpan->_fileId);

			// 建立id和span的映射，方便central cache回收小块内存时，查找对应的span
			for (PAGE_ID i = 0; i < (kSpan->_n << (MY_PAGE_SHIFT - PAGE_SHIFT)); ++i) {
				node->_pageMap[(kSpan->_spanPos >> PAGE_SHIFT) + i] = kSpan;
			}

			kSpan->_isUse = true;

			return kSpan;
		}

		// 检查后面桶是否有span, 如果有就将大号span切分
		for (size_t i = k + 1; i < NPAGES; ++i) {
			if (_spanLists[i].Empty())
				// if (_spanLists[i].empty())
				continue;

			Span* bigSpan = _spanLists[i].PopFront();
			// Span* bigSpan = _spanLists[i].front();
			// _spanLists[i].pop_front();
			Span* kSpan = new Span;

			// 在nSpan头部切一个k页
			kSpan->_headPtr = bigSpan->_headPtr;
			kSpan->_n = k;
			kSpan->_fileId = bigSpan->_fileId;
			kSpan->_spanPos = bigSpan->_spanPos;

			// 更新nSpan的信息，减去k页
			bigSpan->_headPtr = reinterpret_cast<void*>(reinterpret_cast<char*>(bigSpan->_headPtr) + (k << MY_PAGE_SHIFT));
			bigSpan->_n -= k;
			bigSpan->_spanPos += (k * (1 << MY_PAGE_SHIFT));

			// 将切剩下的Span挂到对应大小的位置
			_spanLists[bigSpan->_n].PushFront(bigSpan);
			//_spanLists[bigSpan->_n].push_front(bigSpan);

			ShmServiceNode* node = _shmManger->GetShmNodeByFileId(bigSpan->_fileId);

			// 存储nSpan的首页和尾页与nSpan的映射，方便PageCache回收合并内存时查找对应nSpan
			node->_pageMap[bigSpan->_spanPos >> PAGE_SHIFT] = bigSpan;
			node->_pageMap[(bigSpan->_spanPos >> PAGE_SHIFT) + (bigSpan->_n << (MY_PAGE_SHIFT - PAGE_SHIFT)) - 1] = bigSpan;


			// 建立id和span的映射，方便central cache回收小块内存时，查找对应的span
			for (size_t i = 0; i < (kSpan->_n << (MY_PAGE_SHIFT - PAGE_SHIFT)); ++i) {
				node->_pageMap[(kSpan->_spanPos >> PAGE_SHIFT) + i] = kSpan;
			}

			kSpan->_isUse = true;

			return kSpan;
		}

		// 没有大页的Span了, 只能找堆要一个最大页的Span
		Span* bigSpan = new Span;
		ShmServiceNode* shmNode = _shmManger->GetShmMemory((NPAGES - 1) << MY_PAGE_SHIFT);
		if (!shmNode) {
			LOG_CYC(ERROR, "获取大小为: %ld 的内存失败", k); // 日志记录内存获取失败

			return nullptr;  // 内存获取失败，返回nullptr
		}

		bigSpan->_headPtr = shmNode->_shmSvr->GetShmHeader();
		bigSpan->_fileId = shmNode->_shmFileId;
		bigSpan->_spanPos = 0;
		bigSpan->_n = NPAGES - 1;

		// 将Span插入最大页的桶中，此时列表里已经拥有可以切分的大块页了
		// 只需重新调用一次NewSpan()
		_spanLists[bigSpan->_n].PushFront(bigSpan);
		//_spanLists[bigSpan->_n].push_front(bigSpan);

		//LOG_CYC(_DEBUG, "最大节点头部地址：%p", bigSpan->_headPtr);


		return NewSpan(k);
	}

	// 释放空闲span回到Pagecache，并合并相邻的span
	void ReleaseSpanToPageCache(Span* span) {
		// 处理大于128页的Span
		if (span->_n > NPAGES - 1) {
			_shmManger->ReleaseShmMemory(span->_fileId);
			delete span;

			return;
		}

		ShmServiceNode* shmNode = _shmManger->GetShmNodeByFileId(span->_fileId);

		// 向前合并
		while ((span->_spanPos >> PAGE_SHIFT) - 1 >= 0) {
			Span* prevSpan = shmNode->_pageMap[(span->_spanPos >> PAGE_SHIFT) - 1];

			assert(prevSpan != nullptr);
			assert(prevSpan->_n + span->_n <= NPAGES - 1);

			if (prevSpan->_isUse == true) // 邻居正在使用
				break;

			span->_headPtr = prevSpan->_headPtr;
			span->_n += prevSpan->_n;
			span->_spanPos = prevSpan->_spanPos;

			_spanLists[prevSpan->_n].Erase(prevSpan);
			//_spanLists[prevSpan->_n].remove(prevSpan);

			delete prevSpan;
		}

		// 向后合并
		while ((span->_spanPos >> PAGE_SHIFT) + (span->_n) << (MY_PAGE_SHIFT - PAGE_SHIFT) < (MAX_BYTES * (NPAGES - 1)) >> PAGE_SHIFT) {
			Span* nextSpan = shmNode->_pageMap[(span->_spanPos >> PAGE_SHIFT) + ((span->_n) << (MY_PAGE_SHIFT - PAGE_SHIFT))];

			assert(nextSpan != nullptr);
			assert(nextSpan->_n + span->_n <= NPAGES - 1);

			if (nextSpan->_isUse == true) // 邻居正在使用
				break;

			span->_n += nextSpan->_n;

			_spanLists[nextSpan->_n].Erase(nextSpan);
			//_spanLists[nextSpan->_n].remove(nextSpan);

			delete nextSpan;
		}

		// 页缓存区只留一个空闲最大页就行
		if (span->_n == NPAGES - 1 && !_spanLists[span->_n].Empty()) {
			//if (span->_n == NPAGES - 1 && !_spanLists[span->_n].empty()) {
			_shmManger->ReleaseShmMemory(span->_fileId);

			delete span;

			return;
		}

		// 合并出的大span，插入到对应的链表中
		_spanLists[span->_n].PushFront(span);
		//_spanLists[span->_n].push_front(span);

		span->_isUse = false;

		// 更新id和span的映射
		shmNode->_pageMap[span->_spanPos >> PAGE_SHIFT] = span;
		shmNode->_pageMap[(span->_spanPos >> PAGE_SHIFT) + (span->_n << (MY_PAGE_SHIFT - PAGE_SHIFT)) - 1] = span;
	}

private:
	ShmPageCache() {
		_shmManger = ShmServiceManger::GetInstance();

		// 启动时预分配一个最大管理页
		ReleaseSpanToPageCache(NewSpan(NPAGES-1));

		LOG_CYC(_DEBUG, "预分配一个最大管理页", nullptr);

	}

	~ShmPageCache() {}

	// 禁用拷贝构造函数和赋值操作符
	ShmPageCache(const ShmPageCache&) = delete;
	ShmPageCache& operator=(const ShmPageCache&) = delete;

private:
	//std::list<Span*> _spanLists[NPAGES];
	SpanList _spanLists[NPAGES];
	// std::mutex _pageMtx;

	ShmServiceManger* _shmManger = nullptr;

	static ShmPageCache* _instance; // 静态实例指针
	static std::mutex _mutex;       // 互斥锁
};

// 初始化静态成员
ShmPageCache* ShmPageCache::_instance = nullptr;
std::mutex ShmPageCache::_mutex;